﻿// Originally contributed by Chinajade.
//
// LICENSE:
// This work is licensed under the
//     Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
// also known as CC-BY-NC-SA.  To view a copy of this license, visit
//      http://creativecommons.org/licenses/by-nc-sa/3.0/
// or send a letter to
//      Creative Commons // 171 Second Street, Suite 300 // San Francisco, California, 94105, USA.

#region Usings
using System.Linq;

using Styx;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;
#endregion


namespace Honorbuddy.QuestBehaviorCore
{
	public class PetControl
	{
		/// <summary>
		/// <para>Returns true if you can cast the PETACTIONNAME; otherwise, false.</para>
		/// <para>This method checks for both spell existence, and if the spell is on cooldown.</para>
		/// <para>Notes:<list type="bullet">
		/// 
		/// <item><description><para>* To return 'true', the PETACTIONNAME spell must be hot-barred!  Existence in the spellbook
		/// is insufficient.  This is a limitation of the WoWclient and HB APIs.</para></description></item>
		/// </list></para>
		/// </summary>
		/// <param name="petActionName">may not be null. Examples include: "Follow", "Attack", "Passive", "Growl".</param>
		/// <returns></returns>
		// 24Feb2013-07:42UTC chinajade
		public static bool CanCastPetAction(WoWPetSpell petAction)
		{
			if (petAction == null)
				{ return false; }
			if ((petAction.SpellType == WoWPetSpell.PetSpellType.Spell) && (petAction.Spell == null))
				{ return false; }

			// NB: We can't issue pet directives while mounted, so don't try...
			if (Me.Mounted)
				{ return false; }

			return (petAction.SpellType == WoWPetSpell.PetSpellType.Spell)
				? !petAction.Spell.Cooldown
				: true;
		}


		/// <summary>
		/// <para>Casts the PETACTIONNAME on WOWUNIT.</para>
		/// <para>Notes:<list type="bullet">
		/// <item><description><para>* The PETACTIONNAME spell must be hot-barred!  Existence in the spellbook
		/// is insufficient.  This is a limitation of the WoWclient and HB APIs.</para></description></item>
		/// <item><description><para>* If PETACTIONNAME doesn't exist, or is not hot-barred
		/// an error message is emitted.  To avoid this, use CanCastPetAction() as an entry condition to use of this method.</para></description></item>
		/// <item><description><para>* If PETACTIONNAME is on cooldown, no action is performed.</para></description></item>
		/// </list></para>
		/// </summary>
		/// <param name="petActionName">may not be null. Examples include: "Follow", "Attack", "Passive", "Growl".</param>
		/// <param name="wowUnit">may not be null</param>
		// 24Feb2013-07:42UTC chinajade
		public static void CastPetAction(WoWPetSpell petAction, WoWUnit wowUnit = null)
		{
			if (petAction != null)
			{
				var petActionName = GetPetActionName(petAction);

				if (wowUnit == null)
				{
					QBCLog.DeveloperInfo("Instructing pet to \"{0}\".", petActionName);
					Lua.DoString("CastPetAction({0})", petAction.ActionBarIndex +1);
				}

				else if (wowUnit.IsValid)
				{
					QBCLog.DeveloperInfo("Instructing pet \"{0}\" on {1}.", petActionName, wowUnit.SafeName);
					var originalFocus = Me.FocusedUnitGuid;
					StyxWoW.Me.SetFocus(wowUnit);
					Lua.DoString("CastPetAction({0}, 'focus')", petAction.ActionBarIndex + 1);
					StyxWoW.Me.SetFocus(originalFocus);
				}

				else
				{
					QBCLog.DeveloperInfo("Attempt to instruct pet to \"{0}\" failed--WoWUnit is not valid.",
						petActionName);
				}
			}
		}


		// 30Jun2013-07:42UTC chinajade
		public static WoWPetSpell FindPetActionByName(string petActionName)
		{
			var petAction = Me.PetSpells.FirstOrDefault(p => p.ToString() == petActionName);

			if (petAction == null)
			{
				QBCLog.MaintenanceError("or [USER ERROR]: PetAction('{0}') is either not known, or not hot-barred.",
					petActionName);
			}

			return petAction;
		}


		// 30Jun2013-07:42UTC chinajade
		public static string GetPetActionName(WoWPetSpell petAction)
		{
			if (petAction != null)
			{
				switch (petAction.SpellType)
				{
					case WoWPetSpell.PetSpellType.Action:
						return petAction.Action.ToString();

					case WoWPetSpell.PetSpellType.Spell:
						return petAction.Spell.Name;

					case WoWPetSpell.PetSpellType.Stance:
						return petAction.Stance.ToString();
				}
				return "WoWPetSpell(UNKNOWN)";
			}
			return "WoWPetSpell(NULL)";
		}


		/// <summary>
		/// <para>Returns true if the pet is executing the PETACTIONNAME.</para>
		/// <para>The way the WoWclient works, because a PetAction is active doesn't necessarily mean it is being immediately obeyed.</para>
		/// <para>For instance, a pet's "Passive" ability may be active, but the pet may be attacking due to an explicit "Attack" command temporarily overriding it.</para>
		/// <para>During the attack, the "Passive" ability still shows active.</para>
		/// <para>Notes:<list type="bullet">
		/// <item><description><para>* The PETACTIONNAME spell must be hot-barred!  Existence in the spellbook
		/// is insufficient.  This is a limitation of the WoWclient and HB APIs.</para></description></item>
		/// <item><description><para>* If PETACTIONNAME doesn't exist, or is not hot-barred, 'false' is returned.</para></description></item>
		/// </list></para>
		/// </summary>
		/// <param name="petAction">may not be null. Examples include: "Follow", "Attack", "Passive"</param>
		/// <returns></returns>
		// 24Feb2013-07:42UTC chinajade
		public static bool IsPetActionActive(WoWPetSpell petAction)
		{
			if (petAction == null)
				{ return false; }

			return Lua.GetReturnVal<bool>(string.Format("return GetPetActionInfo({0})", petAction.ActionBarIndex + 1), 4);
		}


		// 15May2013-07:42UTC chinajade
		public static bool CanPetBeDismissed()
		{
			return Me.GotAlivePet && Lua.GetReturnVal<bool>("return PetCanBeDismissed()", 0);
		}


		// 15May2013-07:42UTC chinajade
		public static void PetDismiss()
		{
			if (CanPetBeDismissed())
				{ Lua.DoString("PetDismiss()"); } 

			// Remove totems (or DK's ghoul--which is Totem #1)...
			// Ref: http://wowprogramming.com/docs/api/DestroyTotem
			Lua.DoString("DestroyTotem(1); DestroyTotem(2); DestroyTotem(3); DestroyTotem(4);");

			// TODO: Need to eliminate minions here, also
		}


		/// <summary>
		/// <para>Sends the user's pet to attack the target identified by WOWUNITDELEGATE.</para>
		/// <para>Notes:<list type="bullet">
		/// <item><description><para>* This behavior performs all appropriate checks: pet exists and is alive, target is viable and not friendly, etc.</para></description></item>
		/// <item><description><para>* The 'attack' command will continue to be issued until the pet obeys (by targeting the mob).</para></description></item>
		/// <item><description><para>* The pet's "Attack" command must be hot-barred!  Existence in the spellbook
		/// is insufficient.  This is a limitation of the WoWclient and HB APIs.</para></description></item>
		/// <item><description><para>* The returned Composite is suitable for use in a behavior tree (Priority)Selector container
		/// (i.e., placing it in a Sequence container will not yield the desired results).</para></description></item>
		/// </list></para>
		/// </summary>
		/// <param name="wowUnit">may not be null</param>
		/// <returns>a behavior tree Composite suitable for use in a (Priority)Selector container</returns>
		public static void Attack(WoWUnit wowUnit)
		{
			WoWPetSpell attackAction = _petActionAttack ?? (_petActionAttack = FindPetActionByName("Attack"));

			if (Me.GotAlivePet
				&& (Me.Pet.CurrentTarget != wowUnit)
				&& Query.IsViableForFighting(wowUnit)
				&& CanCastPetAction(attackAction))
			{
				CastPetAction(attackAction, wowUnit);
			}
		}
		private static WoWPetSpell _petActionAttack;


		/// <summary>
		/// <para>Instructs the user's pet to follow its owner.</para>
		/// <para>Notes:<list type="bullet">
		/// <item><description><para>* This behavior performs all appropriate checks: pet exists and is alive, etc.</para></description></item>
		/// <item><description><para>* If the pet is attacking a mob, the 'follow' command will continue to be issued until the pet obeys.</para></description></item>
		/// <item><description><para>* The pet's "Follow" command must be hot-barred!  Existence in the spellbook
		/// is insufficient.  This is a limitation of the WoWclient and HB APIs.</para></description></item>
		/// <item><description><para>* The returned Composite is suitable for use in a behavior tree (Priority)Selector container
		/// (i.e., placing it in a Sequence container will not yield the desired results).</para></description></item>
		/// </list></para>
		/// </summary>
		/// <returns>a behavior tree Composite suitable for use in a (Priority)Selector container</returns>
		public static void Follow()
		{
			WoWPetSpell followAction = _petActionFollow ?? (_petActionFollow = FindPetActionByName("Follow"));

			if (Me.GotAlivePet
				&& (!IsPetActionActive(followAction) || Me.Pet.GotTarget)
				&& CanCastPetAction(followAction))
			{
				CastPetAction(followAction);
			}
		}
		private static WoWPetSpell _petActionFollow;
		

		/// <summary>
		/// <para>Instructs the user's pet to assume one of the following stances: "Assist", "Defensive", "Passive"</para>
		/// <para>Notes:<list type="bullet">
		/// <item><description><para>* This behavior performs all appropriate checks: pet exists and is alive, etc.</para></description></item>
		/// <item><description><para>* The pet's "Assist", "Defensive", and "Passive" commands must be hot-barred!  Existence in the spellbook
		/// is insufficient.  This is a limitation of the WoWclient and HB APIs.</para></description></item>
		/// <item><description><para>* The returned Composite is suitable for use in a behavior tree (Priority)Selector container
		/// (i.e., placing it in a Sequence container will not yield the desired results).</para></description></item>
		/// </list></para>
		/// </summary>
		/// <returns></returns>
		public static void SetStance_Assist()
		{
			if (Me.GotAlivePet)
				{ SetStance_Internal(_petStanceAssist ?? (_petStanceAssist = FindPetActionByName("Assist"))); }
		}
		private static WoWPetSpell _petStanceAssist;


		public static void SetStance_Defensive()
		{
			if (Me.GotAlivePet)
				{ SetStance_Internal(_petStanceDefensive ?? (_petStanceDefensive = FindPetActionByName("Defensive"))); }
		}
		private static WoWPetSpell _petStanceDefensive;


		public static void SetStance_Passive()
		{
			if (Me.GotAlivePet)
				{ SetStance_Internal(_petStancePassive ?? (_petStancePassive = FindPetActionByName("Passive"))); }
		}
		private static WoWPetSpell _petStancePassive;


		private static void SetStance_Internal(WoWPetSpell petStance)
		{
			if (!IsPetActionActive(petStance) && CanCastPetAction(petStance))
				{ CastPetAction(petStance); }
		}

		private static LocalPlayer Me { get { return StyxWoW.Me; } }
	}
}